#include "wl_ext_recorder_pcm.h"
#include "wl_ext_ringbuffer.h"
#include "pthread.h"
#include <SDL2/SDL.h>

// Define the audio format
#define RECORD_PCM_RATE 16000
#define RECORD_PCM_CHANNELS 1
#define RECORD_PCM_FORMAT AUDIO_S16LSB

// Define the buffer size
#define RECORD_PCM_BUFFER_SIZE (6400)

// Define the recorder handle
typedef struct {
    wl_ext_record_callback callback;
    void* pContext;
    SDL_AudioDeviceID device;
    ring_buffer_hdl ringBuffer;
    pthread_mutex_t mutex; 
    unsigned int bufferSize;
    unsigned char *buffer;
} wl_ext_recorder_handle_impl;

static int global_sampleRate = 0;

/**
 * init recorder
 * 
 * @param sampleRate sample rate
 * @param sampleFramesEach sample count each time, when it is 1, it mean bytes of one frame pcm, when it is 2, it mean bytes of two frame pcm
 *                       for example, if samplerate is 16000, and sampleFramesEach is 2, it means pcm data is 640 * 2 bytes
 * @return 0 success, < 0 fail
 */
int wl_ext_record_init(int sampleRate) {
    int result = 0;
    global_sampleRate = sampleRate;
    if (!(SDL_WasInit(0) & SDL_INIT_AUDIO)) {
        if (SDL_Init(SDL_INIT_AUDIO) != 0) {
            printf("SDL_Init fail, %s", SDL_GetError());
            result = -1;
        }
    }
    return result;
}

/**
 * deinit recorder
 * 
 * @return 0 success, < 0 fail  
 */
int wl_ext_record_deinit(void) {
    if (SDL_WasInit(0) & SDL_INIT_AUDIO) {
        SDL_QuitSubSystem(SDL_INIT_AUDIO);
    }
    return 0;
}

void SDLCALL sdl_record_audio_callback(void *userdata, Uint8 *stream, int len) {
    wl_ext_recorder_handle_impl* handle = (wl_ext_recorder_handle_impl*)userdata;

    pthread_mutex_lock(&handle->mutex);
    int emptyBefore = ring_buffer_is_empty(handle->ringBuffer);
    ring_buffer_write(handle->ringBuffer, stream, len);
    pthread_mutex_unlock(&handle->mutex);

    if (emptyBefore) {
        if (handle->callback) {
            handle->callback(handle, WL_EXT_RECORD_DATA, handle->pContext);
        }
    }
}

/**
 * start recorder
 * 
 * @param pHandle recorder handle, when success, it will return recorder handle, else it will return NULL
 * @param callback recorder callback
 * @param pContext recorder context
 * @return 0 success, < 0 fail
 * 
 */
wl_ext_recorder_handle wl_ext_record_start(wl_ext_record_callback callback, void* pContext) {
    // Start the audio device
    wl_ext_recorder_handle_impl* handle = malloc(sizeof(wl_ext_recorder_handle_impl));
    if (!handle) {
        return NULL;
    }

    memset(handle, 0, sizeof(wl_ext_recorder_handle_impl));

    handle->buffer = (unsigned char *)malloc(RECORD_PCM_BUFFER_SIZE);
    handle->bufferSize = RECORD_PCM_BUFFER_SIZE;
    ring_buffer_init(&handle->ringBuffer, handle->buffer, RECORD_PCM_BUFFER_SIZE);
    pthread_mutex_init(&handle->mutex, NULL);

    // Open the audio device
    SDL_AudioSpec spec, outspec;

    memset(&spec, 0, sizeof(SDL_AudioSpec));
    memset(&outspec, 0, sizeof(SDL_AudioSpec));
    spec.freq = global_sampleRate;
    spec.format = RECORD_PCM_FORMAT;
    spec.channels = RECORD_PCM_CHANNELS;
    spec.samples = global_sampleRate / 50;
    spec.callback = sdl_record_audio_callback;
    spec.userdata = handle;

    int count = SDL_GetNumAudioDevices(SDL_TRUE);
    for (int i = 0; i < count; i++) {
        printf("SDL_GetAudioDeviceName: %s\n", SDL_GetAudioDeviceName(i, SDL_TRUE));
        SDL_CloseAudioDevice(i);
    }

    // Set the callback function
    handle->callback = callback;
    handle->pContext = pContext;    


    SDL_AudioDeviceID device = SDL_OpenAudioDevice(SDL_GetAudioDeviceName(0, SDL_TRUE), SDL_TRUE, &spec, &outspec, SDL_AUDIO_ALLOW_FORMAT_CHANGE);
    if (device == 0) {
        return NULL;
    }

    handle->device = device;
    SDL_PauseAudioDevice(handle->device, 0);

    if (handle->callback) {
        handle->callback(handle, WL_EXT_RECORD_STARTED, handle->pContext);
    }

    printf("wl_ext_record_start handle is %p\n", handle);

    return handle;
}

/**
 * stop recorder
 * 
 * @param handle recorder handle
 * @return 0 success, < 0 fail  
 */
int wl_ext_record_stop(wl_ext_recorder_handle handle) {
    wl_ext_recorder_handle_impl *impl = (wl_ext_recorder_handle_impl*)handle;
    SDL_PauseAudioDevice(impl->device, 1);
    SDL_CloseAudioDevice(impl->device);

    if (impl->callback) {
        impl->callback(impl, WL_EXT_RECORD_STOPPED, impl->pContext);
    }

    printf("count is %d\n", ring_buffer_get_count(impl->ringBuffer));

    ring_buffer_deinit(impl->ringBuffer);
    pthread_mutex_destroy(&impl->mutex);
    free(impl);
    return 0;
}

/**
 * get recorder data
 * 
 * @param handle recorder handle
 * @param pPcmData pcm data in byte format  
 * @param pcmLen  bytes of pcm data
 * @return > 0 means bytes actually read, < 0 means error 
 */
int wl_ext_record_get_data(wl_ext_recorder_handle handle, unsigned char *pPcmData, unsigned int pcmLen) {
    wl_ext_recorder_handle_impl *impl = (wl_ext_recorder_handle_impl*)handle;
    int bytes_read = 0;
    // Get the recorded data from the buffer
    pthread_mutex_lock(&impl->mutex);
    int count = ring_buffer_get_count(impl->ringBuffer);
    if (count > pcmLen) {
        count = pcmLen;
    }
    
    bytes_read = ring_buffer_read(impl->ringBuffer, pPcmData, count);
    pthread_mutex_unlock(&impl->mutex);

    return bytes_read;
}

